﻿using Orleans;
using Orleans.Concurrency;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Storage;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

namespace iTool.ClusterComponent
{
    public interface ISetReader : iToolServiceWithStringKey
    {
        Task<bool> ExistsAsync(string payload);
        Task<ICollection<string>> GetAsync();
        Task SetAsync(string payload);
        Task RemoveAsync(string payload);
        Task RemoveAsync();

        /// <summary>
        /// 求并集
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        Task<ICollection<string>> GetUnionAsync(string[] keys);

        /// <summary>
        /// 求交集
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        Task<ICollection<string>> GetIntersectAsync(string[] keys);

        /// <summary>
        /// 差集
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        Task<ICollection<string>> GetDifferencesAsync(string[] keys);
    }


    public class SetReader : iToolServiceBase<SetState, SetExcuteBase>, ISetReader
    {
        public SetReader() : base(1000) { }

        public Task RemoveAsync(string payload)
        {
            this._RemoveAsync(payload);
            this.RaiseEvent(new SetRemoveBytes { Payload = payload });
            return Task.CompletedTask;
        }

        public void _RemoveAsync(string payload)
        {
            if (this.State.keyValuePairs?.ContainsKey(payload) == true)
            {
                this.State.keyValuePairs.TryRemove(payload, out _);
            }
        }


        public Task RemoveAsync()
        {
            this.RaiseEvent(new SetRemove());
            return Task.CompletedTask;
        }

        public Task SetAsync(string payload)
        {
            this._SetAsync(payload);
            this.RaiseEvent(new Set { Payload = payload });
            return Task.CompletedTask;
        }

        public void _SetAsync(string payload)
        {
            if (!this.State.keyValuePairs?.ContainsKey(payload) == true)
            {
                this.State.keyValuePairs.TryAdd(payload, 0);
            }
        }

        public async Task<bool> ExistsAsync(string payload)
        {
            var service = this.GrainFactory.GetGrain<ISetStorageService>(this.GetPrimaryKeyString());
            return await service.ExistsAsync(payload);
        }

        public Task<ICollection<string>> GetAsync()
        {
            return Task.FromResult(this.State.keyValuePairs?.Keys);
        }

        public async Task<ICollection<string>> GetDifferencesAsync(string[] keys)
        {
            var result = new List<string>();
            if (this.State.keyValuePairs?.Keys.Count > 0)
            {
                if (keys.Length > 0)
                {
                    result = this.State.keyValuePairs.Keys.ToList();

                    var primaryKey = this.GetPrimaryKeyString();
                    foreach (var key in keys)
                    {
                        if (key != primaryKey)
                        {
                            var storageService = this.GrainFactory.GetGrain<ISetReader>(key);
                            var list = await storageService.GetAsync();
                            if (list.Count > 0)
                            {
                                result = result.Where(item => !list.Contains(item)).ToList();
                                if (result.Count == 0)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }

        public async Task<ICollection<string>> GetIntersectAsync(string[] keys)
        {
            var result = new List<string>();
            if (this.State.keyValuePairs?.Keys.Count > 0)
            {
                if (keys.Length > 0)
                {
                    var primaryKey = this.GetPrimaryKeyString();
                    foreach (var key in keys)
                    {
                        if (key != primaryKey)
                        {
                            var storageService = this.GrainFactory.GetGrain<ISetReader>(key);
                            var list = await storageService.GetAsync();
                            if (list.Count > 0)
                            {
                                result = result.Concat(this.State.keyValuePairs.Keys.Where(item => list.Contains(item))).ToList();

                                if (result.Count == this.State.keyValuePairs.Keys.Count)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return result;

        }

        public async Task<ICollection<string>> GetUnionAsync(string[] keys)
        {
            var result = this.State.keyValuePairs?.Keys.ToList();

            result = result ?? new List<string>();

            if (keys.Length > 0)
            {
                var primaryKey = this.GetPrimaryKeyString();
                foreach (var key in keys)
                {
                    if (key != primaryKey)
                    {
                        var storageService = this.GrainFactory.GetGrain<ISetReader>(key);
                        var list = await storageService.GetAsync();
                        if (list.Count > 0)
                        {
                            result = result.Concat(list).ToList();
                        }
                    }
                }
            }

            return result.Distinct().ToList();
        }

        public async override Task<bool> ApplyUpdatesToStorage(IReadOnlyList<SetExcuteBase> updates, int expectedversion)
        {
            var service = this.GrainFactory.GetGrain<ISetStorageService>(this.GetPrimaryKeyString());
            var serviceVersion = await service.GetVersionAsync();
            if (expectedversion < serviceVersion)
            {
                return false;
            }
            return true;
        }

        public async override Task<KeyValuePair<int, SetState>> ReadStateFromStorage()
        {
            var service = this.GrainFactory.GetGrain<ISetStorageService>(this.GetPrimaryKeyString());
            var state = await service.GetStateAsync();
            return new KeyValuePair<int, SetState>(state.Version, state);
        }

        protected async override void TransitionState(SetState state, SetExcuteBase @event)
        {
            var service = this.GrainFactory.GetGrain<ISetStorageService>(this.GetPrimaryKeyString());
            if (@event is Set set)
            {
                await service.SetAsync(set.Payload);
            }
            else if (@event is SetRemoveBytes removeField)
            {
                await service.RemoveAsync(removeField.Payload);
            }
            else
            {
                await service.RemoveAsync();
            }
        }

        protected override void NotifyStateChanged(IGrainFactory iGrainFactory)
        {
            //var service = iGrainFactory.GetGrain<ISetStorageService>(this.GetPrimaryKeyString());
            //var state = await service.GetStateAsync();
            //this.State.keyValuePairs = state.keyValuePairs;
        }
    }


    public class SetExcuteBase
    {

    }

    public class SetRemove : SetExcuteBase
    {

    }

    public class SetRemoveBytes : SetExcuteBase
    {
        public string Payload { get; set; }
    }

    public class Set : SetExcuteBase
    {
        public string Payload { get; set; }
    }
}
